package com.moli.iov.alarm.service.impl;

import com.moli.iov.alarm.model.dto.AlarmLocationDto;
import com.moli.iov.alarm.model.dto.AlarmMileageInfoDto;
import com.moli.iov.alarm.model.dto.AlarmVehicleInfoDto;
import com.moli.iov.alarm.model.dto.req.AlarmInfoReqDto;
import com.moli.iov.alarm.model.dto.res.AlarmInfoResDto;
import com.moli.iov.alarm.model.po.AlarmInfo;
import com.moli.iov.alarm.model.po.StatAlarm;
import com.moli.iov.alarm.repositories.AlarmDao;
import com.moli.iov.alarm.repositories.StatAlarmDao;
import com.moli.iov.alarm.service.stat.AlarmStatService;
import com.moli.iov.exception.BusinessException;
import com.moli.iov.util.CommonUtils;
import com.moli.iov.util.DateUtil;
import com.moli.iov.util.StringUtil;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static com.moli.iov.alarm.constants.Constants.END_TIME_RANGE;
import static com.moli.iov.alarm.constants.Constants.START_TIME_RANGE;
import static com.moli.iov.alarm.model.po.StatAlarm.*;

@Slf4j
@Service
public class AlarmStatServiceImpl implements AlarmStatService {

    @Autowired
    protected AlarmDao alarmDao;

    @Autowired
    protected StatAlarmDao statAlarmDao;

    /**
     * 查询当前获取正在报警列表
     * @param alarmInfoDto
     * @return
     */
    @Override
    public List<AlarmInfoResDto> listAlarmInfo(AlarmInfoReqDto alarmInfoDto) {
        List<AlarmInfo> alarmInfos =
                alarmDao.find(assemblyConditions(alarmInfoDto));
        return convertDto(alarmInfos);
    }

    /**
     * 组装条件
     * @param alarmInfoDto
     * @return
     */
    private Query assemblyConditions(AlarmInfoReqDto alarmInfoDto) {

        Criteria criteria = new Criteria();
        //报警中
        criteria.and("status").is(1);

        if(alarmInfoDto.getEndTime() != null){
            criteria.and("endTime").gte(alarmInfoDto.getEndTime());
        }else{
            Instant instant = Instant.now();
            Instant currentDate = Instant.parse(String.format("%d-%d-%dT0:0:0.00Z",
                    instant.get(ChronoField.YEAR),
                    instant.get(ChronoField.MONTH_OF_YEAR),
                    instant.get(ChronoField.DAY_OF_MONTH)));
            criteria.and("endTime").gte(new Date(currentDate.toEpochMilli()));
        }
        Query query = new Query(criteria);
        query.with(new Sort(Sort.Direction.DESC, "beginTime"));

        return query;
    }

    /**
     * 查询当前获取正在报警列表
     * @param currentDate
     * @return
     */
    @Override
    public List<AlarmInfo> resolveAlarmInfo(String currentDate) {
        return alarmDao.find(assemblyStatConditions(currentDate));
    }

    /**
     * 组装条件
     * @param currentDate
     * @return
     */
    private Query assemblyStatConditions(String currentDate) {
        Date beginTime = null, endTime = null;
        try {
            beginTime = DateUtil.parser(DateUtil.YEAR_SECOND, String.format("%s %s", currentDate, START_TIME_RANGE));
            endTime = DateUtil.parser(DateUtil.YEAR_SECOND, String.format("%s %s", currentDate, END_TIME_RANGE));
        } catch (ParseException e) {
            log.error("Current date parse find a fail, {}", e);
        }
        Criteria criteria = new Criteria();
        //报警中
        Criteria begin = Criteria.where("beginTime").lte(endTime).gte(beginTime).and("endTime").exists(false).and("status").is(1);
        Criteria end = Criteria.where("beginTime").lt(beginTime).and("endTime").exists(false).and("status").is(1);
        criteria.orOperator(begin, end).and("companyId").exists(true);
        Query query = new Query(criteria);
        query.with(new Sort(Sort.Direction.DESC, "beginTime"));
        return query;
    }

    @Override
    public StatAlarm saveStatAlarm(StatAlarm statAlarm){
        Long companyId = statAlarm.getCompanyId();
        String date = statAlarm.getDate();
        if(companyId == null){
            throw new BusinessException("企业ID不能为空！");
        }
        if(StringUtil.isBlank(date)){
            throw new BusinessException("非法的报警统计日期！");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("companyId").is(companyId));
        query.addCriteria(Criteria.where("date").is(date));
        Update update = new Update();
        update.setOnInsert(COMPANY_ID, companyId);
        update.setOnInsert(ALARM_DATE, date);
        update.set(OUT_FENCE_ALARM_COUNT, statAlarm.getOutFenceAlarmCount());
        update.set(IN_FENCE_ALARM_COUNT, statAlarm.getInFenceAlarmCount());
        update.set(MILEAGE_EXCEPTION_CHANGE_COUNT, statAlarm.getMileageExceptionChangeCount());
        update.set(OFF_LINE_COUNT, statAlarm.getOffLineCount());
        update.set(ONE_LEVEL_ALARM_COUNT, statAlarm.getOneLvAlarmCount());
        update.set(TWO_LEVEL_ALARM_COUNT, statAlarm.getTwoLvAlarmCount());
        update.set(THREE_LEVEL_ALARM_COUNT, statAlarm.getThreeLvAlarmCount());
        update.set(VEHICLE_COUNT, statAlarm.getVehicleCount());
        FindAndModifyOptions options = FindAndModifyOptions.options();
        options.upsert(true);
        options.returnNew(true);
        return statAlarmDao.update(query, update, options, StatAlarm.class);
    }

    /**
     * List转换
     * @param alarmInfos
     * @return
     */
    private List<AlarmInfoResDto> convertDto(List<AlarmInfo> alarmInfos) {
        List<AlarmInfoResDto> alarmInfoDtos = new ArrayList<>();
        if (alarmInfos == null) {
            return alarmInfoDtos;
        }
        alarmInfos.forEach(alarmInfo -> {
            AlarmInfoResDto alarmInfoResDto = new AlarmInfoResDto();
            AlarmLocationDto alarmLocationDto = new AlarmLocationDto();
            AlarmMileageInfoDto alarmMileageInfoDto = new AlarmMileageInfoDto();
            AlarmVehicleInfoDto alarmVehicleInfoDto = new AlarmVehicleInfoDto();

            CommonUtils.copyProperties(alarmInfo, alarmInfoResDto);
            CommonUtils.copyProperties(alarmInfo.getLocation(), alarmLocationDto);
            CommonUtils.copyProperties(alarmInfo.getAlarmMileageInfo(), alarmMileageInfoDto);
            CommonUtils.copyProperties(alarmInfo.getAlarmVehicleInfo(), alarmVehicleInfoDto);

            alarmInfoResDto.setLocation(alarmLocationDto);
            alarmInfoResDto.setAlarmMileageInfo(alarmMileageInfoDto);
            alarmInfoResDto.setAlarmVehicleInfo(alarmVehicleInfoDto);

            alarmInfoDtos.add(alarmInfoResDto);
        });
        return alarmInfoDtos;
    }

}
